package com.xuecheng.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.api.search.dto.CoursePubIndexDTO;
import com.xuecheng.api.search.qo.QueryCoursePubModel;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.search.common.constant.ContentSearchErrorCode;
import com.xuecheng.search.service.CoursePubSearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * 课程搜索服务实现层(es原始Api实现)
 */
@Service
public class CoursePubSearchServiceImpl implements CoursePubSearchService {


    @Qualifier("restHighLevelClient")
    @Autowired
    private RestHighLevelClient client;
    @Value("${xuecheng.elasticsearch.course.index}")
    private String index;
    @Value("${xuecheng.elasticsearch.condition}")
    private String queryCondition;

    /**
     * <p>
     * 分页 条件查询发布的课程
     * </p>
     *
     * @param params 分页参数封装
     * @param model  条件参数封装
     * @return PageVO<CoursePubIndexDTO>
     */
    @Override
    public PageVO<CoursePubIndexDTO> queryCoursePubIndexWithCondition(PageRequestParams params, QueryCoursePubModel model) {
        //判断分页参数
        Integer pageSize = params.getPageSize();
        Long pageNo = params.getPageNo();
        //判断是否为正整数
        if (isNotPositiveNumeric(String.valueOf(pageSize))) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        if (isNotPositiveNumeric(String.valueOf(pageNo))) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }

        //构建查询请求
        SearchRequest request = createSearchRequest(params, model);
        //解析响应数据并返回
        return getResponseData(request, params);
    }

    /**
     * <p>
     * 根据课程发布id查询发布信息
     * </p>
     *
     * @param coursePubId 课程发布id
     * @return CoursePubIndexDTO
     */
    @Override
    public CoursePubIndexDTO queryCoursePubById(Long coursePubId) {
        try {
            String sourceAsString = getSearchObjStr(coursePubId);

            CoursePubIndexDTO dto = JSON.parseObject(sourceAsString, CoursePubIndexDTO.class);
            dto.setIndexId(coursePubId);
            return dto;
        } catch (IOException e) {
            ExceptionCast.castWithException(ContentSearchErrorCode.E_150001, e);
        }
        return null;
    }

    private String getSearchObjStr(Long coursePubId) throws IOException {
        GetRequest getRequest = new GetRequest(index, String.valueOf(coursePubId));

        GetResponse documentFields = client.get(getRequest, RequestOptions.DEFAULT);

        return documentFields.getSourceAsString();
    }

    /**
     * <p>
     * 微服务调用
     * </p>
     *
     * @param coursePubId 课程发布id
     * @return RestResponse<CoursePubIndexDTO>
     */
    @Override
    public RestResponse<CoursePubIndexDTO> getCoursePubById(Long coursePubId) {
        try {
            String searchObjStr = getSearchObjStr(coursePubId);
            CoursePubIndexDTO dto = null;

            if (StringUtils.isNotBlank(searchObjStr)) {
                dto = JSON.parseObject(searchObjStr, CoursePubIndexDTO.class);
                dto.setIndexId(coursePubId);
            }
            return RestResponse.success(dto);

        } catch (IOException e) {
            return RestResponse.validfail(ContentSearchErrorCode.E_150001);
        }
    }

    private PageVO<CoursePubIndexDTO> getResponseData(SearchRequest request, PageRequestParams params) {
        SearchResponse search = null;
        try {
            search = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            ExceptionCast.castWithException(ContentSearchErrorCode.E_150001, e);
        }
        SearchHits hits = search.getHits();
        long total = hits.getTotalHits().value;
        PageVO<CoursePubIndexDTO> pageVO = new PageVO<>(total, params.getPageNo(), params.getPageSize());
        SearchHit[] dataHits = hits.getHits();
        List<CoursePubIndexDTO> list = new ArrayList<>();
        //从查询结果中获取数据
        for (SearchHit dataHit : dataHits) {
            String id = dataHit.getId();
            String sourceAsString = dataHit.getSourceAsString();
            CoursePubIndexDTO dto = JSON.parseObject(sourceAsString, CoursePubIndexDTO.class);
            dto.setIndexId(Long.valueOf(id));

            //获取高亮文字的数据
            Map<String, HighlightField> highlightFields = dataHit.getHighlightFields();
            HighlightField nameField = highlightFields.get("name");

            if (!ObjectUtils.isEmpty(nameField)) {
                Text[] fragments = nameField.getFragments();
                StringBuilder nameString = new StringBuilder();
                for (Text fragment : fragments) {
                    nameString.append(fragment);
                }
                dto.setName(nameString.toString());
            }
            HighlightField descriptionField = highlightFields.get("description");
            if (!ObjectUtils.isEmpty(descriptionField)) {
                Text[] fragments = descriptionField.getFragments();
                StringBuilder descStr = new StringBuilder();
                for (Text fragment : fragments) {
                    descStr.append(fragment);
                }
                dto.setDescription(descStr.toString());
            }
            list.add(dto);
        }
        pageVO.setItems(list);
        return pageVO;
    }

    private SearchRequest createSearchRequest(PageRequestParams params, QueryCoursePubModel model) {
        //构建查询请求对象
        SearchRequest request = new SearchRequest(index);

        // 2.构建搜索源信息对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        int pageNo = params.getPageNo().intValue();
        int pageSize = params.getPageSize();
        //构建分页数据
        sourceBuilder.size(pageSize).from((pageNo - 1) * pageSize);
        //构建布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //放入关键字进行分词查询
        String[] split = queryCondition.split(",");
        String keyword = model.getKeyword();

        if (StringUtils.isNotBlank(keyword)) {
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, split).field("name", 10)
                    .minimumShouldMatch("50%");
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }
        //放入精准匹配查询条件 ，不为空才放入
        //使用过滤器提高查询效率
        String grade = model.getGrade();
        if (StringUtils.isNotBlank(grade)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", grade));
        }

        String st = model.getSt();
        if (StringUtils.isNotBlank(st)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("st", st));
        }

        String mt = model.getMt();
        if (StringUtils.isNotBlank(mt)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt", mt));
        }
        //设置高亮属性 红色加粗
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color=red><b>");
        highlightBuilder.postTags("</b></font>");
        List<HighlightBuilder.Field> fields = highlightBuilder.fields();
        fields.add(new HighlightBuilder.Field("name"));
        fields.add(new HighlightBuilder.Field("description"));
        //放入高亮条件
        sourceBuilder.highlighter(highlightBuilder);
        //放入查询条件
        sourceBuilder.query(boolQueryBuilder);
        //把搜索源放入request中
        request.source(sourceBuilder);
        return request;
    }


    /**
     * <p>
     * 校验分页参数
     * </p>
     *
     * @param str 数据字符串
     * @return boolean
     */
    private static boolean isNotPositiveNumeric(String str) {
        String regex = "^[1-9]\\d*$";
        return !str.matches(regex);
    }

}